package com.mimo.common.exception.handler;

import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.mimo.common.exception.AccessLimitException;
import com.mimo.common.exception.ApplicationException;
import com.mimo.common.result.BaseResult;
import com.mimo.common.result.error.AccessLimitErrorResult;
import com.mimo.common.result.error.MissMandatoryFieldResult;
import com.mimo.common.utils.JsonUtils;

/**
 * 高层抽取定义那些异常收集的基础方法
 * 
 * @author Hongyu
 */
public abstract class AbstractExceptionHandler {

  private static final Logger logger = LoggerFactory.getLogger(AbstractExceptionHandler.class);

  protected static final BaseResult ERROR_RESULT = BaseResult.ERROR_RET;

  @ExceptionHandler
  @ResponseBody
  public ResponseEntity<String> defaultErrorHandler(HttpServletRequest request, Exception e) {

    ApplicationException ae = wrapper(e);

    BaseResult ret = specificExceptionHandler(ae);

    switch (ae.getLevel()) {
      case ERROR:
        logger.error("", ae);
        break;
      case WARNING:
        logger.warn("", ae);
        break;
      case INFO:
        logger.info("", ae);
        break;
      default:
        break;
    }

    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setContentType(MediaType.APPLICATION_JSON);
    return new ResponseEntity<>(JsonUtils.toJsonString(ret), responseHeaders, HttpStatus.OK);
  }

  /**
   * 子类需要对特定异常处理的时候，应该继承并重写该方法
   * 
   * @param ae
   * @return
   */
  protected BaseResult specificExceptionHandler(ApplicationException ae) {

    BaseResult ret = ERROR_RESULT;

    if (ae.getCause() instanceof BindException) {
      ret = collectErrorMessage(BindException.class.cast(ae.getCause()).getBindingResult());
    } else if (ae.getCause() instanceof MethodArgumentNotValidException) {
      ret = collectErrorMessage(MethodArgumentNotValidException.class.cast(ae.getCause()).getBindingResult());
    } else if (ae.getCause() instanceof ConstraintViolationException) {
      ret = collectErrorMessage(ConstraintViolationException.class.cast(ae.getCause()).getConstraintViolations());
    } else if (ae instanceof AccessLimitException) {
      ret = new AccessLimitErrorResult();
    }

    return ret;
  }

  /**
   * 对于验证类的信息，统一收集到一个公共处理器上
   * 
   * @param result
   * @return
   */
  private BaseResult collectErrorMessage(Set<ConstraintViolation<?>> result) {

    if (!result.isEmpty()) {
      return new MissMandatoryFieldResult(
          result.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining("\r\n")));
    }
    return ERROR_RESULT;
  }

  /**
   * 对于验证类的信息，统一收集到一个公共处理器上
   * 
   * @param rs
   * @return
   */
  private BaseResult collectErrorMessage(BindingResult rs) {

    if (rs.hasErrors()) {
      return new MissMandatoryFieldResult(
          rs.getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining("\r\n")));
    }
    return ERROR_RESULT;
  }

  /**
   * 对任意的异常先统一包装到自定义高层异常类型
   * 
   * @param e
   * @return
   */
  private ApplicationException wrapper(Exception e) {
    ApplicationException ae;
    if (!(e instanceof ApplicationException)) {
      ae = new ApplicationException(e);
    } else {
      ae = ApplicationException.class.cast(e);
    }
    return ae;
  }
}
